<!-- Copyright 2023 OpenObserve Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
-->

<!-- eslint-disable vue/v-on-event-hyphenation -->
<!-- eslint-disable vue/attribute-hyphenation -->
<template>
  <q-page>
    <div v-if="!showAddJSTransformDialog">
      <div class="tw-w-full tw-h-full tw-pr-[0.625rem] tw-pb-[0.625rem]">
        <div class="card-container tw-mb-[0.625rem]">
          <div class="tw-flex tw-items-center tw-justify-between tw-py-3 tw-px-4 tw-h-[68px]">
            <div class="q-table__title tw-font-[600]">
                {{ t("function.enrichmentTables") }}
              </div>
              <div class="q-ml-auto" data-test="enrichment-tables-search-input">
                <q-input
                  v-model="filterQuery"
                  borderless
                  dense
                  class="q-ml-auto no-border o2-search-input tw-w-[220px]"
                  :placeholder="t('function.searchEnrichmentTable')"

                >
                  <template #prepend>
                    <q-icon class="o2-search-input-icon" name="search" />
                  </template>
                </q-input>
              </div>
              <q-btn
                class="q-ml-sm o2-primary-button tw-h-[36px]"
                no-caps
                flat
                :label="t(`function.addEnrichmentTable`)"
                @click="showAddUpdateFn({})"
              />
          </div>
        </div>
        <div class="tw-w-full tw-h-full tw-pb-[0.625rem]">
          <div class="card-container tw-h-[calc(100vh-127px)]">
            <q-table
              ref="qTable"
              :rows="visibleRows"
              :columns="columns"
              row-key="id"
              :pagination="pagination"
              :filter="filterQuery"
              style="width: 100%"
              :style="hasVisibleRows
                  ? 'width: 100%; height: calc(100vh - 127px)' 
                  : 'width: 100%'"
              class="o2-quasar-table o2-row-md o2-quasar-table-header-sticky "
            >
              <template #no-data>
                <NoData />
              </template>
              <template v-slot:body-cell-actions="props">
                <q-td :props="props">
                  <q-btn
                    :data-test="`${props.row.name}-explore-btn`"
                    :title="t('logStream.explore')"
                    padding="sm"
                    unelevated
                    size="sm"
                    round
                    flat
                    @click="exploreEnrichmentTable(props)"
                    icon="search"
                  >
                </q-btn>
                  <q-btn
                    icon="list_alt"
                    :title="t('logStream.schemaHeader')"
                    padding="sm"
                    unelevated
                    size="sm"
                    round
                    flat
                    @click="listSchema(props)"
                  >
                </q-btn>
                  <q-btn
                    padding="sm"
                    unelevated
                    size="sm"
                    icon="edit"
                    round
                    flat
                    :title="t('function.enrichmentTables')"
                    @click="showAddUpdateFn(props)"
                  >
                </q-btn>
                  <q-btn
                    padding="sm"
                    unelevated
                    size="sm"
                    round
                    :icon="outlinedDelete"
                    flat
                    :title="t('function.delete')"
                    @click="showDeleteDialogFn(props)"
                  >
                </q-btn>
                </q-td>
              </template>

              <template v-slot:body-cell-function="props">
                <q-td :props="props">
                  <q-tooltip>
                    <pre>{{ props.row.function }}</pre>
                  </q-tooltip>
                  <pre style="white-space: break-spaces">{{
                    props.row.function
                  }}</pre>
                </q-td>
              </template>

              <template #bottom="scope">
                <div class="tw-flex tw-items-center tw-justify-end tw-w-full tw-h-[48px]">
                  <div class="o2-table-footer-title tw-flex tw-items-center tw-w-[200px] tw-mr-md">
                    {{ resultTotal }} {{ t('function.enrichmentTables') }}
                  </div>
                <QTablePagination
                  :scope="scope"
                  :position="'bottom'"
                  :resultTotal="resultTotal"
                  :perPageOptions="perPageOptions"
                  @update:changeRecordPerPage="changePagination"
                />
                </div>
              </template>
              <template v-slot:header="props">
                  <q-tr :props="props">
                    <!-- Rendering the of the columns -->
                    <!-- here we can add the classes class so that the head will be sticky -->
                    <q-th
                      v-for="col in props.cols"
                      :key="col.name"
                      :props="props"
                      :class="col.classes"
                      :style="col.style"
                    >
                      {{ col.label }}
                    </q-th>
                  </q-tr>
                </template>
            </q-table>
          </div>
        </div>
      </div>
    </div>
    <div v-else>
      <add-enrichment-table
        v-model="formData"
        :isUpdating="isUpdated"
        @update:list="refreshList"
        @cancel:hideform="hideForm"
      />
    </div>
    <ConfirmDialog
      title="Delete Enrichment Table"
      message="Are you sure you want to delete enrichment table?"
      @update:ok="deleteLookupTable"
      @update:cancel="confirmDelete = false"
      v-model="confirmDelete"
    />
    <q-dialog
      v-model="showEnrichmentSchema"
      position="right"
      full-height
      maximized
    >
      <EnrichmentSchema :selectedEnrichmentTable="selectedEnrichmentTable" />
    </q-dialog>
  </q-page>
</template>

<script lang="ts">
import { computed, defineComponent, onBeforeMount, onMounted, ref, watch } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { useQuasar, type QTableProps } from "quasar";
import { useI18n } from "vue-i18n";

import QTablePagination from "../shared/grid/Pagination.vue";
import AddEnrichmentTable from "./AddEnrichmentTable.vue";
import NoData from "../shared/grid/NoData.vue";
import ConfirmDialog from "../ConfirmDialog.vue";
import segment from "../../services/segment_analytics";
import {
  formatSizeFromMB,
  getImageURL,
  verifyOrganizationStatus,
} from "../../utils/zincutils";
import streamService from "@/services/stream";
import { outlinedDelete } from "@quasar/extras/material-icons-outlined";
import useStreams from "@/composables/useStreams";
import EnrichmentSchema from "./EnrichmentSchema.vue";
import { useReo } from "@/services/reodotdev_analytics";

export default defineComponent({
  name: "EnrichmentTableList",
  components: {
    QTablePagination,
    AddEnrichmentTable,
    NoData,
    ConfirmDialog,
    EnrichmentSchema,
  },
  emits: [
    "updated:fields",
    "update:changeRecordPerPage",
    "update:maxRecordToReturn",
  ],
  setup(props, { emit }) {
    const store = useStore();
    const { t } = useI18n();
    const $q = useQuasar();
    const router = useRouter();
    const jsTransforms: any = ref([]);
    const formData: any = ref({});
    const showAddJSTransformDialog: any = ref(false);
    const qTable: any = ref(null);
    const selectedDelete: any = ref(null);
    const isUpdated: any = ref(false);
    const confirmDelete = ref<boolean>(false);
    const showEnrichmentSchema = ref<boolean>(false);
    const filterQuery = ref("");
    const { track } = useReo();
    const columns: any = ref<QTableProps["columns"]>([
      {
        name: "#",
        label: "#",
        field: "#",
        align: "left",
        style: "width: 67px",
      },
      {
        name: "name",
        field: "name",
        label: t("function.name"),
        align: "left",
        sortable: true,
      },
      {
        name: "doc_num",
        field: (row: any) => row.doc_num.toLocaleString(),
        label: t("logStream.docNum"),
        align: "left",
        sortable: true,
        sort: (a, b, rowA, rowB) => {
          return parseInt(rowA.doc_num) - parseInt(rowB.doc_num);
        },
        style: "width: 150px",
      },
      {
        name: "storage_size",
        label: t("logStream.storageSize"),
        field: (row: any) => formatSizeFromMB(row.storage_size),
        align: "left",
        sortable: true,
        sort: (a, b, rowA, rowB) => {
          return rowA.original_storage_size - rowB.original_storage_size;
        },
        style: "width: 150px",
      },
      {
        name: "compressed_size",
        field: (row: any) => formatSizeFromMB(row.compressed_size),
        label: t("logStream.compressedSize"),
        align: "left",
        sortable: false,
        sort: (a, b, rowA, rowB) =>
          rowA.original_compressed_size- rowB.original_compressed_size,
        style: "width: 150px",
      },

      {
        name: "actions",
        field: "actions",
        label: t("function.actions"),
        align: "center",
        sortable: false,
        classes: "actions-column",
      },
    ]);
    const { getStreams, resetStreamType, getStream } = useStreams();

    onBeforeMount(() => {
      getLookupTables();
    });
    //here we need to check if the action is there or not 
    //because if action is there user before refresh the page user was there in add / update page
    //so to maitain consistency we are checking the action and if action is there we are showing the add / update page
    //else we are showing the list of enrichment tables
    onMounted(()=>{
      //it is for showing empty add page when user refresh the page
      if(router.currentRoute.value.query.action === "add"){
        showAddUpdateFn({})
      }
      //it is for showing the update page when user refresh the page
      //we are passing the name of the enrichment table to the update page
      else if(router.currentRoute.value.query.action === "update"){
        showAddUpdateFn({
          row: {
            name: router.currentRoute.value.query.name,
          }
        })
      }
    })

    const getLookupTables = (force: boolean = false) => {
      const dismiss = $q.notify({
        spinner: true,
        message: "Please wait while loading enrichment tables...",
      });

      getStreams("enrichment_tables", false, false, force)
        .then((res: any) => {
          let counter = 1;
          resultTotal.value = res.list.length;
          jsTransforms.value = res.list.map((data: any) => {
            let doc_num = "";
            let storage_size = "";
            let compressed_size = "";
            let original_storage_size = "";
            let original_compressed_size = "";

            if (data.stats) {
              doc_num = data.stats.doc_num;
              storage_size = data.stats.storage_size + " MB";
              compressed_size = data.stats.compressed_size + " MB";
              original_storage_size = data.stats.storage_size;
              original_compressed_size = data.stats.compressed_size;
            }
            return {
              "#": counter <= 9 ? `0${counter++}` : counter++,
              id: data.name + counter,
              name: data.name,
              doc_num: doc_num,
              storage_size: storage_size,
              compressed_size: compressed_size,
              original_storage_size: original_storage_size,
              original_compressed_size: original_compressed_size,
              actions: "action buttons",
              stream_type: data.stream_type,
            };
          });
          dismiss();
        })
        .catch((err) => {
          console.info("Error while fetching enrichment tables", err);
          dismiss();
          if (err.response.status != 403) {
            $q.notify({
              type: "negative",
              message:
                err.response?.data?.message ||
                "Error while fetching functions.",
              timeout: 2000,
            });
          }
        });
    };

    const perPageOptions: any = [
      { label: "20", value: 20 },
      { label: "50", value: 50 },
      { label: "100", value: 100 },
      { label: "250", value: 250 },
      { label: "500", value: 500 },
    ];

    const resultTotal = ref<number>(0);
    const maxRecordToReturn = ref<number>(100);
    const selectedPerPage = ref<number>(20);
    const selectedEnrichmentTable = ref<any>(null);
    const pagination: any = ref({
      rowsPerPage: 20,
    });
    const changePagination = (val: { label: string; value: any }) => {
      selectedPerPage.value = val.value;
      pagination.value.rowsPerPage = val.value;
      qTable.value.setPagination(pagination.value);
    };

    const addLookupTable = () => {
      showAddJSTransformDialog.value = true;
    };

    const showAddUpdateFn = (props: any) => {
      formData.value = props.row;
      let action;
      if (!props.row) {
        isUpdated.value = false;
        action = "Add Enrichment Table";
        router.push({
          name: "enrichmentTables",
          query: {
            action: "add",
            org_identifier: store.state.selectedOrganization.identifier,
          },
        });
        track("Button Click", {
          button: "Add Enrichment Table",
          page: "Functions"
        });
      } else {
        isUpdated.value = true;
        action = "Update Enrichment Table";
        router.push({
          name: "enrichmentTables",
          query: {
            action: "update",
            name: props.row.name,
            org_identifier: store.state.selectedOrganization.identifier,
          },
        });
        track("Button Click", {
          button: "Update Enrichment Table",
          page: "Functions"
        });
      }
      addLookupTable();

      segment.track("Button Click", {
        button: action,
        user_org: store.state.selectedOrganization.identifier,
        user_id: store.state.userInfo.email,
        page: "Functions",
      });
    };

    const refreshList = () => {
      router.push({
        name: "enrichmentTables",
        query: {
          org_identifier: store.state.selectedOrganization.identifier,
        },
      });
      showAddJSTransformDialog.value = false;
      resetStreamType("enrichment_tables");
      getLookupTables(true);
    };

    const hideForm = () => {
      showAddJSTransformDialog.value = false;
      router.replace({
        name: "enrichmentTables",
        query: {
          org_identifier: store.state.selectedOrganization.identifier,
        },
      });
    };

    const deleteLookupTable = () => {
      streamService
        .delete(
          store.state.selectedOrganization.identifier,
          selectedDelete.value.name,
          "enrichment_tables",
        )
        .then((res: any) => {
          if (res.data.code == 200) {
            $q.notify({
              color: "positive",
              message: `${selectedDelete.value.name} deleted successfully.`,
            });
            resetStreamType("enrichment_tables");
            getLookupTables(true);
          }
        })
        .catch((err: any) => {
          if (err.response.status != 403) {
            $q.notify({
              color: "negative",
              message:
                err.response?.data?.message || "Error while deleting stream.",
            });
          }
        });

      segment.track("Button Click", {
        button: "Delete Enrichment Table",
        user_org: store.state.selectedOrganization.identifier,
        user_id: store.state.userInfo.email,
        function_name: selectedDelete.value.name,
        is_ingest_func: selectedDelete.value.ingest,
        page: "Functions",
      });
    };

    const showDeleteDialogFn = (props: any) => {
      selectedDelete.value = props.row;
      confirmDelete.value = true;
    };

    /**
     * Get time range for stream explorer, for enrichment tables it will get the time range from the stream data min and max time
     * @param stream: Stream object
     */
    const getTimeRange = async (stream: any) => {
      const dateTime: { period?: string; from?: number; to?: number } = {};

      const dismiss = $q.notify({
        spinner: true,
        message: "Redirecting to explorer...",
        color: "secondary",
      });

      try {
        await getStream(stream.name, stream.stream_type, true)
          .then((streamResponse) => {
            if (
              streamResponse.stats.doc_time_min &&
              streamResponse.stats.doc_time_max
            ) {
              //reducing the doc_time_min by 1000000 to get the exact time range
              //previously we were subtracting 60000000 which might confuse some users so we are using 1000000 (1sec)
              dateTime["from"] = streamResponse.stats.doc_time_min - 1000000;
              //adding 60000000(1min)
              dateTime["to"] = streamResponse.stats.doc_time_max + 60000000;
            } else if (streamResponse.stats.created_at) {
              // When enrichment table is uploaded, stats will not have doc_time_min and doc_time_max.
              // Stats will be available asynchronously, so we can use created_at time to get the time range.
              dateTime["from"] = streamResponse.stats.created_at - 60000000;
              dateTime["to"] = streamResponse.stats.created_at + 3600000000;
            } else {
              dateTime["period"] = "15m";
            }
          })
          .catch((err) => {
            console.error("Error while getting enrichment table: ", err);
            dateTime["period"] = "15m";
          })
          .finally(() => {
            dismiss();
          });
      } catch (err) {
        console.error("Error while getting enrichment table: ", err);
        dateTime["period"] = "15m";
        dismiss();
      }

      return dateTime;
    };

    const exploreEnrichmentTable = async (props: any) => {
      store.dispatch("logs/setIsInitialized", false);
      const timestamps = await getTimeRange(props.row);
      router.push({
        name: "logs",
        query: {
          stream_type: props.row.stream_type,
          stream: props.row.name,
          refresh: "0",
          query: "",
          type: "stream_explorer",
          org_identifier: store.state.selectedOrganization.identifier,
          ...timestamps,
        },
      });
    };
    const listSchema = async (props: any) => {
      selectedEnrichmentTable.value = props.row.name;
      showEnrichmentSchema.value = true;
    };
    const filterData = (rows: any, terms: any) => {
        var filtered = [];
        terms = terms.toLowerCase();
        for (var i = 0; i < rows.length; i++) {
          if (rows[i]["name"].toLowerCase().includes(terms)) {
            filtered.push(rows[i]);
          }
        }
        return filtered;
      };

    const visibleRows = computed(() => {
      if (!filterQuery.value) return jsTransforms.value || []
      return filterData(jsTransforms.value || [], filterQuery.value)
    });
    const hasVisibleRows = computed(() => visibleRows.value.length > 0);

    // Watch visibleRows to sync resultTotal with search filter
    watch(visibleRows, (newVisibleRows) => {
      resultTotal.value = newVisibleRows.length;
    }, { immediate: true });
    return {
      t,
      qTable,
      store,
      router,
      jsTransforms,
      columns,
      formData,
      hideForm,
      confirmDelete,
      selectedDelete,
      getLookupTables,
      pagination,
      resultTotal,
      refreshList,
      perPageOptions,
      selectedPerPage,
      addLookupTable,
      deleteLookupTable,
      isUpdated,
      showAddUpdateFn,
      showDeleteDialogFn,
      changePagination,
      maxRecordToReturn,
      showAddJSTransformDialog,
      outlinedDelete,
      filterQuery,
      filterData,
      getImageURL,
      verifyOrganizationStatus,
      exploreEnrichmentTable,
      showEnrichmentSchema,
      listSchema,
      selectedEnrichmentTable,
      getTimeRange,
      visibleRows,
      hasVisibleRows,
    };
  },
  computed: {
    selectedOrg() {
      return this.store.state.selectedOrganization.identifier;
    },
  },
  watch: {
    selectedOrg(newVal: any, oldVal: any) {
      this.verifyOrganizationStatus(
        this.store.state.organizations,
        this.router,
      );
      if (
        (newVal != oldVal || this.jsTransforms.value == undefined) &&
        this.router.currentRoute.value.name == "pipeline"
      ) {
        this.resultTotal = 0;
        this.jsTransforms = [];
        this.getLookupTables(true);
      }
    },
  },
});
</script>

<style lang="scss">

.search-en-table-input {
  .q-field__inner {
    width: 250px;
  }
}
</style>
